package com.xbongbong.paas.script.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasProcessDataEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.PaySheetTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.model.PaasProcessDataModel;
import com.xbongbong.paas.pojo.dto.InitFundMenuDTO;
import com.xbongbong.paas.pojo.dto.PaymentSheetDataDTO;
import com.xbongbong.paas.pojo.dto.PaymentSheetExplainsSingleDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.script.help.ScriptHelper;
import com.xbongbong.paas.script.pojo.dto.FixSheetDTO;
import com.xbongbong.paas.script.pojo.dto.InitCustomerStatementDTO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.XbbResponse;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.XbbProConstant;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.fundfixdata.pojo.dto.FundFixDataDTO;
import com.xbongbong.pro.script.pojo.vo.ScriptVO;
import com.xbongbong.pro.service.toolbox.statistic.help.ChartQueryHelp;
import com.xbongbong.pro.statistic.constant.ChartConstant;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.FundAccountEntity;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.domain.entity.ext.PayPlanEntityExt;
import com.xbongbong.saas.domain.entity.ext.PayPlanSheetEntityExt;
import com.xbongbong.saas.domain.entity.ext.PaymentSheetEntityExt;
import com.xbongbong.saas.enums.ForbiddenSettingEnum;
import com.xbongbong.saas.enums.SetTypeEnum;
import com.xbongbong.saas.enums.ShowTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.PayPlanEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentDistributorEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentRelationshipEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.model.ContractModel;
import com.xbongbong.saas.model.CustomerStatementModel;
import com.xbongbong.saas.model.FundAccountModel;
import com.xbongbong.saas.model.PayPlanModel;
import com.xbongbong.saas.model.PayPlanSheetModel;
import com.xbongbong.saas.model.PaymentSheetModel;
import com.xbongbong.saas.script.help.InitExplainsHelper;
import com.xbongbong.saas.script.tempenum.TempContractEnum;
import com.xbongbong.saas.script.tempenum.TempCustomerManagementEnum;
import com.xbongbong.saas.script.tempenum.TempInvoiceEnum;
import com.xbongbong.saas.script.tempenum.TempPaySheetEnum;
import com.xbongbong.saas.script.tempenum.TempPaymentEnum;
import com.xbongbong.saas.script.tempenum.TempRefundEnum;
import com.xbongbong.saas.service.FundFixDataService;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.model.CompanyModel;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 回款单、付款单脚本处理
 * @author zcp
 * @version v1.0
 * @since v1.0
 */
@RestController
@RequestMapping(XbbProConstant.API_ROOT_PREFIX + "/script/fund/sheet")
public class FundSheetScriptController {
    private static final Logger LOG = LoggerFactory.getLogger(FundSheetScriptController.class);
    @Resource
    private ProBaseConfig proBaseConfig;
    @Resource
    private CompanyModel companyModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private ScriptHelper scriptHelper;
    @Resource
    private PayPlanSheetModel payPlanSheetModel;
    @Resource
    private PayPlanModel payPlanModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaasProcessDataModel paasProcessDataModel;
    @Resource
    private FundAccountModel fundAccountModel;
    @Resource
    private PaymentSheetModel paymentSheetModel;
    @Resource
    private ChartQueryHelp chartQueryHelp;
    @Resource
    private EsHelper esHelper;
    @Resource
    private FundFixDataService fundFixDataService;
    @Resource
    private CustomerStatementModel customerStatementModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private ContractModel contractModel;
    /**
     * 回款单关联合同、关联应收款异常格式处理
     * 关联合同、关联应收款，核销金额，回款类型，归属人等处理
     * @param paymentSheetExplainsSingleDTO
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/paymentSheet/exception/format", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String paymentSheetExceptionFormat(@RequestBody @Valid PaymentSheetExplainsSingleDTO paymentSheetExplainsSingleDTO) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(paymentSheetExplainsSingleDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            LOG.warn("-----------paymentSheetExceptionFormat start 开始执行回款单关联合同、关联应收款异常格式处理脚本-----------");
            long timeStart = DateTimeUtil.getInt();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Integer start = 0;
            //每次查询100条
            Integer pageNum = 300;
            Long idGte = 0L;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            param.put("del", 0);
            String attrCon = PaymentSheetEnum.CONTRACT.getAttr();
            String attrPlan = PaymentSheetEnum.PAYMENT.getAttr();
            String attrBelongOld = "text_2";
            String attrBelong = PaymentSheetEnum.BELONG_ID.getAttr();
            while (true) {
                param.put("idGte", idGte);
                LOG.warn("处理进度：已处理到 tb_saas_payment_sheet.id = " + idGte);
                //回款单表单
                List<PaymentSheetEntityExt> list = paymentSheetModel.findEntitys(param);
                if (list == null || list.size() == 0) {
                    break;
                }
                idGte = list.get(list.size() - 1).getId();

                List<UpdateDataEntity> updateList = new ArrayList<>();
                for (PaymentSheetEntityExt entityExt : list) {
                    JSONObject data = entityExt.getData();
                    if (data == null) {
                        continue;
                    }
                    String linkConId = data.getString(attrCon);
                    String linkPlanId = data.getString(attrPlan);
                    String belong = data.getString(attrBelong);
                    boolean flag = false;
                    JSONObject updateData = new JSONObject();
                    if (StringUtil.isNotEmpty(linkConId) && !linkConId.contains(ChartConstant.LEFT_PARENTHESES)) {
                        flag = true;
                        JSONArray conIdArray = new JSONArray();
                        Long conId = StringUtil.StringToLong(linkConId, -1L);
                        conIdArray.add(conId);
                        updateData.put(attrCon, conIdArray);
                    }
                    if (StringUtil.isNotEmpty(linkPlanId) && !linkPlanId.contains(ChartConstant.LEFT_PARENTHESES)) {
                        flag = true;
                        JSONArray planIdArray = new JSONArray();
                        Long planId = StringUtil.StringToLong(linkPlanId, -1L);
                        planIdArray.add(planId);
                        updateData.put(attrPlan, planIdArray);
                    }
                    if (StringUtil.isEmpty(belong)) {
                        //没有归属人信息，则读取资金一期改造前的text_2字段（原归属人字段）
                        Object belongIdObject = data.get(attrBelongOld);
                        String belongId;
                        if (belongIdObject instanceof JSONObject) {
                            JSONObject belongIdJSONObject = (JSONObject) belongIdObject;
                            belongId = belongIdJSONObject.getString("id");
                        } else {
                            belongId = data.getString("text_2");
                        }
                        if (StringUtil.isNotEmpty(belongId)) {
                            flag = true;
                            JSONArray belongIdArray = new JSONArray();
                            belongIdArray.add(belongId);
                            updateData.put(attrBelong, belongIdArray);
                        }
                    }
                    //处理类型字段（text_10）
                    Integer ifBad;
                    Integer ifRed;
                    try {
                        ifBad = data.getInteger("num_2");
                    }catch (Exception e){
                        LOG.error("ifBad --  num_2 get error, error tb_saas_payment_sheet.id=" + entityExt.getId(), e);
                        ifBad = 0;
                    }
                    try {
                        ifRed = data.getInteger("num_3");
                    }catch (Exception e){
                        LOG.error("ifRed --  num_2 get error, error tb_saas_payment_sheet.id=" + entityExt.getId(), e);
                        ifRed = 0;
                    }
                    String paymentSheetType = data.getString(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
                    if (StringUtil.isEmpty(paymentSheetType)) {
                        flag = true;
                        if (Objects.equals(ifBad, BasicConstant.ONE)) {
                            updateData.put(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), PaymentSheetTypeEnum.BAD_PAYMENT.getCode());
                        } else if (Objects.equals(ifRed, BasicConstant.ONE)) {
                            updateData.put(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), PaymentSheetTypeEnum.RED_WRITE_OFF.getCode());
                        } else {
                            updateData.put(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), PaymentSheetTypeEnum.WRITE_OFF.getCode());
                        }
                        //处理核销金额（num_7）
                        double paymentAmount = data.getDoubleValue(PaymentSheetEnum.AMOUNT.getAttr());
                        updateData.put(PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), paymentAmount);
                    }
                    if (flag) {
                        updateList.add(ExplainUtil.getUpdateData(entityExt.getId(), updateData, entityExt.getCorpid(), null));
                    }
                }
                if (updateList.size() > 0) {
                    paymentSheetModel.updateBatchTemp(updateList);
                }
            }
            LOG.warn("-----------time consume paymentSheetExceptionFormat:" + (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/paymentSheet/exception/format over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/paymentSheet/exception/format error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 回款单模板解释：收款账户、支付方式、负责人、协同人处理脚本
     * @param paymentSheetExplainsSingleDTO
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/paymentSheet/explains/some", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String paymentSheetExplains(@RequestBody @Valid PaymentSheetExplainsSingleDTO paymentSheetExplainsSingleDTO) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(paymentSheetExplainsSingleDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            LOG.warn("-----------paymentSheetExplains start 开始执行回款单模板解释：收款账户、支付方式、负责人处理脚本-----------");
            long timeStart = DateTimeUtil.getInt();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Integer start = 0;
            //每次查询100条
            Integer pageNum = 300;
            Long idGte = 0L;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            param.put("del", 0);
            param.put("businessType", XbbRefTypeEnum.PAYMENT_SHEET.getCode());
            //处理回款单的收款账户、支付方式字段
            PaymentSheetEnum fundAccountEnum = PaymentSheetEnum.FUND_ACCOUNT;
            PaymentSheetEnum ownerEnum = PaymentSheetEnum.OWNER_ID;
            PaymentSheetEnum couserEnum = PaymentSheetEnum.COUSERID;
            String attrFundAccount = fundAccountEnum.getAttr();
            String attrPayMethod = PaymentSheetEnum.PAY_METHOD.getAttr();
            String attrOwnerId = ownerEnum.getAttr();
            String attrCouserId = couserEnum.getAttr();
            String attrAmount = PaymentSheetEnum.AMOUNT.getAttr();
            String attrWriteOffAmount = PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr();
            String attrInvoiceAmount = PaymentSheetEnum.INVOICE_AMOUNT.getAttr();
            Class<PaymentSheetEnum> paymentSheetEnumClass = PaymentSheetEnum.class;
            List<String> enumAttrs = InitExplainsHelper.getBusinessEnumAttrs(paymentSheetEnumClass);
            while (true) {
                param.put("idGte", idGte);
                LOG.warn("处理进度：已处理到 tb_paas_form_explain.id = " + idGte);
                //回款单表单解释列表
                List<PaasFormExplainEntity> explainList = paasFormExplainModel.list(param);
                if (explainList == null || explainList.size() == 0) {
                    break;
                }
                idGte = explainList.get(explainList.size() - 1).getId();

                boolean ownerFlag = true;
                boolean couserFlag = true;
                for (PaasFormExplainEntity explainEntity : explainList) {
                    List<FieldAttrEntity> fieldAttrList = JsonHelperUtil.parseArray(explainEntity.getExplains(), FieldAttrEntity.class);
                    for (int i = 0; i < fieldAttrList.size(); i++) {
                        FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                        String attr = fieldAttrEntity.getAttr();
                        if (Objects.equals(attrFundAccount, attr)) {
                            fieldAttrEntity = InitExplainsHelper.setField(paymentSheetEnumClass, enumAttrs, fundAccountEnum);
                            TempPaySheetEnum.initFundAccount(fieldAttrEntity);
                        } else if (Objects.equals(attrPayMethod, attr)) {
                            fieldAttrEntity.setSetType(SetTypeEnum.DISABLE_REPEAT.getCode());
                        } else if (Objects.equals(attrOwnerId, attr)) {
                            ownerFlag = false;
                            fieldAttrEntity = InitExplainsHelper.setField(paymentSheetEnumClass, enumAttrs, ownerEnum);
                            fieldAttrEntity.setIsRedundant(0);
                            fieldAttrEntity.setAttrName(PaymentSheetEnum.OWNER_ID.getAttrName());
                        } else if (Objects.equals(attrCouserId, attr)) {
                            couserFlag = false;
                            fieldAttrEntity = InitExplainsHelper.setField(paymentSheetEnumClass, enumAttrs, couserEnum);
                            fieldAttrEntity.setIsRedundant(0);
                            fieldAttrEntity.setAttrName(PaymentSheetEnum.COUSERID.getAttrName());
                        } else if (Objects.equals(attr, attrAmount) || Objects.equals(attr, attrWriteOffAmount) || Objects.equals(attr, attrInvoiceAmount)) {
                            fieldAttrEntity.setForbiddenSettingList(Collections.singletonList(ForbiddenSettingEnum.DISABLE_DEFAULT.getAlias()));
                        }
                        fieldAttrList.set(i, fieldAttrEntity);
                    }
                    if (ownerFlag) {
                        FieldAttrEntity newFieldAttrEntity = InitExplainsHelper.setField(paymentSheetEnumClass, enumAttrs, ownerEnum);
                        fieldAttrList.add(newFieldAttrEntity);
                    }
                    if (couserFlag) {
                        FieldAttrEntity newFieldAttrEntity = InitExplainsHelper.setField(paymentSheetEnumClass, enumAttrs, couserEnum);
                        fieldAttrList.add(newFieldAttrEntity);
                    }
                    String explains = JSON.toJSONString(fieldAttrList);
                    explainEntity.setExplains(explains);
                }
                paasFormExplainModel.updateBatchExplains(explainList);
            }
            LOG.warn("-----------time consume paymentSheetExplains:" + (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/paymentSheet/explains over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/paymentSheet/explains error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 回/付款单模板解释：协同人处理
     * curl http://pt2jobs.xbongbong.com.cn/pro/v1/script/fund/sheet/explains/coUserId -X POST -H 'Content-type: application/json' -d '{"frontDev": "1","xbbCode": 123456}'
     * @param paymentSheetExplainsSingleDTO
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/explains/coUserId", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String explainsCoUserId(@RequestBody @Valid PaymentSheetExplainsSingleDTO paymentSheetExplainsSingleDTO) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(paymentSheetExplainsSingleDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            LOG.warn("-----------paymentSheetCoUserId start 开始执行回/付款单模板解释：协同人处理脚本-----------");
            long timeStart = DateTimeUtil.getInt();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("del", 0);
            param.put("businessTypeIn", Arrays.asList(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), XbbRefTypeEnum.PAY_SHEET.getCode()));
            Long maxId = paasFormExplainModel.getMaxId(param);
            Integer start = 0;
            //每次查询100条
            Integer pageNum = 300;
            Long idGte = 0L;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            //处理回/付款单的协同人属性
            String attrCouserId = FieldTypeEnum.COUSERID.getAlias();
            while (true) {
                param.put("idGte", idGte);
                LOG.warn("处理进度：已处理到 tb_paas_form_explain.id " + idGte + " / " + maxId);
                //表单解释列表
                List<PaasFormExplainEntity> explainList = paasFormExplainModel.list(param);
                if (explainList == null || explainList.size() == 0) {
                    break;
                }
                idGte = explainList.get(explainList.size() - 1).getId();
                for (PaasFormExplainEntity explainEntity : explainList) {
                    List<FieldAttrEntity> fieldAttrList = JsonHelperUtil.parseArray(explainEntity.getExplains(), FieldAttrEntity.class);
                    for (int i = 0; i < fieldAttrList.size(); i++) {
                        FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                        String attr = fieldAttrEntity.getAttr();
                        if (Objects.equals(attrCouserId, attr)) {
                            fieldAttrEntity.setShowType(ShowTypeEnum.LIST_DETAIL.getCode());
                            fieldAttrEntity.setIsRedundant(0);
                            fieldAttrList.set(i, fieldAttrEntity);
                            break;
                        }
                    }
                    String explains = JSON.toJSONString(fieldAttrList);
                    explainEntity.setExplains(explains);
                }
                paasFormExplainModel.updateBatchExplains(explainList);
            }
            LOG.warn("-----------time consume explainsCoUserId:" + (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/coUserId over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/coUserId error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 资金3期回款单/退货退款/应收款/销项发票/合同/客户模板解释处理
     * curl http://pt2webscript.zjk.taeapp.com/pro/v1/script/fund/sheet/explains/fundThree -X POST -H 'Content-type: application/json' -d '{"frontDev": "1","xbbCode": 123456}'
     * @param initFundMenuDTO
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/explains/fundThree", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String explainsFundThree(@RequestBody @Valid InitFundMenuDTO initFundMenuDTO) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(initFundMenuDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            Integer full = initFundMenuDTO.getFlag();
            List<String> corpidList = initFundMenuDTO.getCorpidList();
            boolean fullFlag = true;
            if (Objects.equals(full, BasicConstant.ZERO)) {
                //非全量必须传入要处理的公司id集合
                if (CollectionsUtil.isEmpty(corpidList)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006, "corpidList不能为空，请传入要部分提前处理的公司id集合");
                }
                fullFlag = false;
            } else {
                if (Objects.isNull(corpidList)) {
                    corpidList = new ArrayList<>();
                }
            }
            LOG.warn("-----------explainsFundThree start 开始执行资金3期回款单/退货退款/合同/销项发票/应收款模板/客户解释-----------");
            long timeStart = DateTimeUtil.getInt();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("del", 0);
            if (!fullFlag) {
                param.put("corpidIn", corpidList);
            }
            Integer businessTypeSheet = XbbRefTypeEnum.PAYMENT_SHEET.getCode();
            Integer businessTypeRefund = XbbRefTypeEnum.REFUND.getCode();
            Integer businessTypePayment = XbbRefTypeEnum.PAYMENT.getCode();
            Integer businessTypeInvoice = XbbRefTypeEnum.INVOICE.getCode();
            Integer businessTypeContract = XbbRefTypeEnum.CONTRACT.getCode();
            Integer businessTypeCustomer = XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode();
            Integer businessTypePaySheet = XbbRefTypeEnum.PAY_SHEET.getCode();
            List<Integer> businessTypeList = Arrays.asList(businessTypeSheet, businessTypeRefund, businessTypePayment, businessTypeInvoice, businessTypeContract, businessTypeCustomer, businessTypePaySheet);
            //移除默认解释缓存
            removeDefaultExplainRedis(businessTypeList);
            param.put("businessTypeIn", businessTypeList);
            //只处理非经销商的（经销商的初始化直接是对的）
            param.put("distributorMark", DistributorMarkEnum.OTHER.getCode());
            Long maxId = paasFormExplainModel.getMaxId(param);
            Integer start = 0;
            //每次查询200条
            Integer pageNum = 200;
            Long idGte = 0L;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            /*
             1、回款单
             （1）关联回款计划 -->关联应收款，并更改其是否可以编辑必填（原来置灰不让设置必填）；关联回款计划隐藏-->关联应收款隐藏；
             （2）关联合同隐藏字段的fieldType更改
             2、退货退款
             （1）增加红冲应收款、关联应收款隐藏
             3、应收款
              (1)修改：回款计划编号 -->应收款编号; 计划收款金额-->应收金额; “回款状态”字段新增状态“已红冲”; 预计回款日期描述：手动创建应收模式下，取应收款预计回款日期；2开票产生应收模式下，根据应收生成日期和账期规则自动生成。
                   “实收金额”增加描述信息：实收金额=【回款单】已核销金额-【回款单】红冲核销金额+【预收款】已核销金额-【预收款】红冲核销金额； “未收金额”增加描述信息：未收金额=应收款-实收金额-坏账金额-丨红冲应收丨
                    “坏账金额”增加描述信息：坏账金额=坏账回款金额总计；“红冲应收金额”增加描述信息：红冲应收金额=红冲应收款金额总计
              (2)增加：红冲应收金额、来源单据、应收生成时间、应收账期
              (3)修改：添加回款计划 --> 添加应收款，attr从array_1改为array_31（独立版上线后移除了array_1，则用户自定义字段可能会占位）
              4、发票
              （1）回款计划 -->应收款；关联回款计划隐藏-->关联应收款隐藏
              5、合同模板
               （1）增加：应收账期
               （2）修改：合同订单中的回款计划名称改为应收款
              6、客户模板
                (1)增加：应收账期
              7、付款单
              （1）更改关联采购合同隐藏的fieldType
             */
            //回款单
            String attrPayment = PaymentSheetEnum.PAYMENT.getAttr();
            String attrPaymentLink = PaymentSheetEnum.PAYMENT_LINK_TEXT.getAttr();
            PaymentSheetEnum enumContractLink = PaymentSheetEnum.CONTRACT_LINK_TEXT;
            //退货退款
            RefundEnum enumPayment = RefundEnum.PAYMENTS;
            RefundEnum enumPaymentLink = RefundEnum.PAYMENTS_LINKED_TEXT;
            Class<RefundEnum> refundEnumClass = RefundEnum.class;
            List<String> refundEnumAttrs = InitExplainsHelper.getBusinessEnumAttrs(refundEnumClass);
            FieldAttrEntity newFieldAttrEntityPayment = InitExplainsHelper.setField(refundEnumClass, refundEnumAttrs, enumPayment);
            TempRefundEnum.initPayments(newFieldAttrEntityPayment);
            FieldAttrEntity newFieldAttrEntityPaymentLink = InitExplainsHelper.setField(refundEnumClass, refundEnumAttrs, enumPaymentLink);

            //==========================下面是对应收款模板的处理
            //HashMap<String, FieldAttrEntity> paymentShouldUpdateHashMap = new HashMap<>();
            //-------------------------修改的attr
            String payAttrSerialNo = PaymentEnum.PAYMENT_NO.getAttr();
            String payAttrAmount = PaymentEnum.AMOUNT.getAttr();
            String payStatus = PaymentEnum.STATUS.getAttr();
            String payEstimateTime = PaymentEnum.ESTIMATE_TIME.getAttr();
            String payRealAmount = PaymentEnum.REAL_AMOUNT.getAttr();
            String payUnAmount = PaymentEnum.UN_AMOUNT.getAttr();
            String payBadAmount = PaymentEnum.BAD_AMOUNT.getAttr();
            PaymentEnum enumAddPayment = PaymentEnum.ADD_PAYMENT;
            HashMap<String, FieldAttrEntity> attrEntityHashMap = getAddFieldMap();

            //===========================下面是对销项发票的处理
            String invoiceLinkPay = InvoiceEnum.PAYMENT_ID.getAttr();
            String invoiceLinkPayText = InvoiceEnum.PAYMENT_ID_LINK_TEXT.getAttr();
            String invoicePaymentRelationship = InvoiceEnum.PAYMENT_RELATIONSHIP.getAttr();

            //==========================下面是对合同模板的处理
            //-----------新增
            Class<ContractEnum> contractEnumClass = ContractEnum.class;
            ContractEnum contractReceivablePeriodEnum = ContractEnum.RECEIVABLE_PERIOD;
            List<String> contractEnumAttrs = InitExplainsHelper.getBusinessEnumAttrs(contractEnumClass);
            FieldAttrEntity newFieldAttrEntityReceivable = InitExplainsHelper.setField(contractEnumClass, contractEnumAttrs, contractReceivablePeriodEnum);
            TempContractEnum.initReceivablePeriod(newFieldAttrEntityReceivable);
            //-----------修改
            ContractEnum enumContractAddPayment = ContractEnum.ADD_PAYMENT;

            //===========================下面是对客户模板的处理
            Class<CustomerManagementEnum> customerManagementEnumClass = CustomerManagementEnum.class;
            CustomerManagementEnum customerManagementReceivablePeriodEnum = CustomerManagementEnum.RECEIVABLE_PERIOD;
            List<String> customerEnumAttrs = InitExplainsHelper.getBusinessEnumAttrs(customerManagementEnumClass);
            FieldAttrEntity newFieldAttrEntityCustomerReceivable = InitExplainsHelper.setField(customerManagementEnumClass, customerEnumAttrs, customerManagementReceivablePeriodEnum);
            TempCustomerManagementEnum.initReceivablePeriod(newFieldAttrEntityCustomerReceivable);
            //===========================下面是对付款单模板的处理
            PaySheetEnum linkPurchase = PaySheetEnum.PURCHASE_LINK_TEXT;
            while (true) {
                param.put("idGte", idGte);
                LOG.warn("处理进度：已处理到 tb_paas_form_explain.id {} / {}", idGte, maxId);
                //表单解释列表
                List<PaasFormExplainEntity> explainList = paasFormExplainModel.list(param);
                if (explainList == null || explainList.size() == 0) {
                    break;
                }
                idGte = explainList.get(explainList.size() - 1).getId();
                List<PaasFormExplainEntity> updateList = new ArrayList<>();
                for (PaasFormExplainEntity explainEntity : explainList) {
                    //全量执行时，剔除已经处理过的公司id集合
                    if (fullFlag && corpidList.contains(explainEntity.getCorpid())) {
                        continue;
                    }
                    if (StringUtil.isEmpty(explainEntity.getExplains()) || Objects.equals("[]", explainEntity.getExplains())) {
                        //如果该公司使用的默认解释（未修改过模板），则不处理。需要同步处理缓存（防止线上有生效时间为一天的原explain缓存）
                        continue;
                    }
                    List<FieldAttrEntity> fieldAttrList = JsonHelperUtil.parseArray(explainEntity.getExplains(), FieldAttrEntity.class);
                    if (CollectionsUtil.isEmpty(fieldAttrList)) {
                        continue;
                    }
                    boolean addFlag = true;
                    if (Objects.equals(explainEntity.getBusinessType(), businessTypeSheet)) {
                        for (int i = 0; i < fieldAttrList.size(); i++) {
                            FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(attrPayment, attr)) {
                                fieldAttrEntity.setAttrName(PaymentSheetEnum.PAYMENT.getAttrName());
                                fieldAttrEntity.setDefaultName(PaymentSheetEnum.PAYMENT.getAttrName());
                                fieldAttrEntity.setSetType(PaymentSheetEnum.PAYMENT.getSetType());
                            } else if (Objects.equals(attrPaymentLink, attr)) {
                                fieldAttrEntity.setAttrName(PaymentSheetEnum.PAYMENT_LINK_TEXT.getAttrName());
                                fieldAttrEntity.setDefaultName(PaymentSheetEnum.PAYMENT_LINK_TEXT.getAttrName());
                            } else if (Objects.equals(enumContractLink.getAttr(), attr)) {
                                fieldAttrEntity.setFieldType(enumContractLink.getFieldType());
                            }
                        }
                    }else if (Objects.equals(explainEntity.getBusinessType(), businessTypePayment)) {
                        for (int i = 0; i < fieldAttrList.size(); i++) {
                            FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(payAttrSerialNo, attr)) {
                                fieldAttrEntity.setAttrName(PaymentEnum.PAYMENT_NO.getAttrName());
                                fieldAttrEntity.setDefaultName(PaymentEnum.PAYMENT_NO.getAttrName());
                            }else if (Objects.equals(payAttrAmount, attr)) {
                                fieldAttrEntity.setAttrName(PaymentEnum.AMOUNT.getAttrName());
                                fieldAttrEntity.setDefaultName(PaymentEnum.AMOUNT.getAttrName());
                            }else if (Objects.equals(payStatus, attr)) {
                                TempPaymentEnum.initStatus(fieldAttrEntity);
                            }else if (Objects.equals(payEstimateTime, attr)) {
                                fieldAttrEntity.setMemo(I18nMessageUtil.getMessage(I18nStringConstant.ESTIMATE_TIME_MEMO));
                            }else if (Objects.equals(payRealAmount, attr)) {
                                fieldAttrEntity.setMemo(I18nMessageUtil.getMessage(I18nStringConstant.REAL_AMOUNT_MEMO));
                            }else if (Objects.equals(payUnAmount, attr)) {
                                fieldAttrEntity.setMemo(I18nMessageUtil.getMessage(I18nStringConstant.UN_AMOUNT_MEMO));
                            }else if (Objects.equals(payBadAmount, attr)) {
                                fieldAttrEntity.setMemo(I18nMessageUtil.getMessage(I18nStringConstant.BAD_AMOUNT_MEMO));
                            }else if (Objects.equals("array_1", attr) || Objects.equals(enumAddPayment.getAttr(), attr)) {
                                fieldAttrEntity.setAttr(enumAddPayment.getAttr());
                                fieldAttrEntity.setAttrName(enumAddPayment.getAttrName());
                                fieldAttrEntity.setDefaultName(enumAddPayment.getAttrName());
                            }else if (attrEntityHashMap.containsKey(attr)) {
                                addFlag = false;
                                fieldAttrList.set(i, attrEntityHashMap.get(attr));
                            }
                        }
                        if (addFlag) {
                            //新增操作
                            for (Map.Entry<String, FieldAttrEntity> entry : attrEntityHashMap.entrySet()) {
                                fieldAttrList.add(entry.getValue());
                            }
                        }
                    }else if(Objects.equals(explainEntity.getBusinessType(), businessTypeInvoice)){
                        for (int i = 0; i < fieldAttrList.size(); i++) {
                            FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(invoiceLinkPay, attr)) {
                                fieldAttrEntity.setAttrName(InvoiceEnum.PAYMENT_ID.getAttrName());
                                fieldAttrEntity.setDefaultName(InvoiceEnum.PAYMENT_ID.getAttrName());
                            }else if (Objects.equals(invoiceLinkPayText, attr)) {
                                fieldAttrEntity.setAttrName(InvoiceEnum.PAYMENT_ID_LINK_TEXT.getAttrName());
                                fieldAttrEntity.setDefaultName(InvoiceEnum.PAYMENT_ID_LINK_TEXT.getAttrName());
                            }else if (Objects.equals(invoicePaymentRelationship, attr)) {
                                List<ItemPoJo> items = fieldAttrEntity.getItems();
                                if (CollectionUtils.isNotEmpty(items)) {
                                    for (ItemPoJo item : items) {
                                        if (Objects.equals(item.getValue(), PaymentRelationshipEnum.PAYMENT.getCode())) {
                                            item.setText(PaymentRelationshipEnum.PAYMENT.getAlias());
                                        }
                                    }
                                } else {
                                    TempInvoiceEnum.initPaymentRelationship(fieldAttrEntity);
                                }
                            }
                        }
                    }else if(Objects.equals(explainEntity.getBusinessType(), businessTypeRefund)){
                        for (int i = 0; i < fieldAttrList.size(); i++) {
                            FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(enumPayment.getAttr(), attr)) {
                                addFlag = false;
                                fieldAttrList.set(i, newFieldAttrEntityPayment);
                            } else if (Objects.equals(enumPaymentLink.getAttr(), attr)) {
                                addFlag = false;
                                fieldAttrList.set(i, newFieldAttrEntityPaymentLink);
                            }
                        }
                        if (addFlag) {
                            //增加红冲应收款、关联应收款隐藏
                            fieldAttrList.add(newFieldAttrEntityPayment);
                            fieldAttrList.add(newFieldAttrEntityPaymentLink);
                        }
                    }else if (Objects.equals(explainEntity.getBusinessType(), businessTypeContract)) {
                        for (int i = 0; i < fieldAttrList.size(); i++) {
                            FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(contractReceivablePeriodEnum.getAttr(), attr)) {
                                addFlag = false;
                                fieldAttrList.set(i, newFieldAttrEntityReceivable);
                            }else if (Objects.equals(enumContractAddPayment.getAttr(), attr)) {
                                fieldAttrEntity.setAttrName(enumContractAddPayment.getAttrName());
                                fieldAttrEntity.setDefaultName(enumContractAddPayment.getAttrName());
                            }
                        }
                        if (addFlag) {
                            //增加应收账期
                            fieldAttrList.add(newFieldAttrEntityReceivable);
                        }
                    }else if (Objects.equals(explainEntity.getBusinessType(), businessTypeCustomer)) {
                        for (int i = 0; i < fieldAttrList.size(); i++) {
                            FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(customerManagementReceivablePeriodEnum.getAttr(), attr)) {
                                addFlag = false;
                                fieldAttrList.set(i, newFieldAttrEntityCustomerReceivable);
                            }
                        }
                        if (addFlag) {
                            //增加应收账期
                            fieldAttrList.add(newFieldAttrEntityCustomerReceivable);
                        }
                    }else if (Objects.equals(explainEntity.getBusinessType(), businessTypePaySheet)) {
                        for (int i = 0; i < fieldAttrList.size(); i++) {
                            FieldAttrEntity fieldAttrEntity = fieldAttrList.get(i);
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(linkPurchase.getAttr(), attr)) {
                                fieldAttrEntity.setFieldType(linkPurchase.getFieldType());
                                break;
                            }
                        }
                    }
                    String explains = JSON.toJSONString(fieldAttrList);
                    explainEntity.setExplains(explains);
                    updateList.add(explainEntity);
                }
                if (CollectionsUtil.isNotEmpty(updateList)) {
                    paasFormExplainModel.updateBatchExplains(updateList);
                }
            }
            LOG.warn("-----------time consume explainsFundThree:{}", (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/explains/fundThree over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/explains/fundThree error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 强制把有改动的表单模板默认解释从缓存移除
     * @param businessTypeList
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void removeDefaultExplainRedis(List<Integer> businessTypeList) {
        String corpid = "0";
        String other = "_" + DistributorMarkEnum.OTHER.getCode();
        String distributor = "_" + DistributorMarkEnum.DISTRIBUTOR.getCode();
        for (Integer businessType : businessTypeList) {
            String redisKey = corpid + "_" + businessType;
            paasRedisHelper.removeValue(RedisPrefixConstant.FIELD_EXPLANATION, redisKey);
            String redisKeyOther = redisKey + other;
            paasRedisHelper.removeValue(RedisPrefixConstant.FIELD_EXPLANATION, redisKeyOther);
            String redisKeyDistributor = redisKey + distributor;
            paasRedisHelper.removeValue(RedisPrefixConstant.FIELD_EXPLANATION, redisKeyDistributor);
        }
    }

    /**
     * 新增的一些fieldAttr
     *
     * @return
     */
    private HashMap<String, FieldAttrEntity> getAddFieldMap() {
        //---------------------------新增的枚举码
        PaymentEnum redAmountEnum = PaymentEnum.RED_AMOUNT;
        PaymentEnum sourceEnum = PaymentEnum.SOURCE;
        PaymentEnum generationDateEnum = PaymentEnum.GENERATION_DATE;
        PaymentEnum receivablePeriodEnum = PaymentEnum.RECEIVABLE_PERIOD;
        PaymentEnum coUserIdEnum = PaymentEnum.COUSERID;
        PaymentEnum receivableIsRedEnum = PaymentEnum.RECEIVABLE_IS_RED;
        PaymentEnum receivableHasRedEnum = PaymentEnum.RECEIVABLE_HAS_RED;
        PaymentEnum sourceRefTypeEnum = PaymentEnum.SOURCE_REF_TYPE;
        PaymentEnum receivablesOriginIdEnum = PaymentEnum.RECEIVABLES_ORIGIN_ID;
        PaymentEnum receivablesOriginLinkTextEnum = PaymentEnum.RECEIVABLES_ORIGIN_LINK_TEXT;
        PaymentEnum sourceLinkTextEnum = PaymentEnum.SOURCE_LINK_TEXT;

        Class<PaymentEnum> paymentEnumClass = PaymentEnum.class;
        List<String> paymentEnumAttrs = InitExplainsHelper.getBusinessEnumAttrs(paymentEnumClass);

        FieldAttrEntity newFieldAttrEntityRedAmount = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, redAmountEnum);
        TempPaymentEnum.initRedAmount(newFieldAttrEntityRedAmount);

        FieldAttrEntity newFieldAttrEntitySource = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, sourceEnum);
        TempPaymentEnum.initSource(newFieldAttrEntitySource);

        FieldAttrEntity newFieldAttrEntityGeneration = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, generationDateEnum);
        TempPaymentEnum.initGenerationDate(newFieldAttrEntityGeneration);

        FieldAttrEntity newFieldAttrEntityReceivable = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, receivablePeriodEnum);
        TempPaymentEnum.initReceivablePeriod(newFieldAttrEntityReceivable);

        FieldAttrEntity newFieldAttrEntityCoUserId = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, coUserIdEnum);

        FieldAttrEntity newFieldAttrEntityReceivableIsRed = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, receivableIsRedEnum);
        TempPaymentEnum.initReceivableIsRed(newFieldAttrEntityReceivableIsRed);

        FieldAttrEntity newFieldAttrEntityReceivableHasRed = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, receivableHasRedEnum);
        TempPaymentEnum.initReceivableHasRed(newFieldAttrEntityReceivableHasRed);

        FieldAttrEntity newFieldAttrEntitySourceRefType = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, sourceRefTypeEnum);

        FieldAttrEntity newFieldAttrEntityreceivablesOriginId = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, receivablesOriginIdEnum);

        FieldAttrEntity newFieldAttrEntityReceivablesOriginLinkText = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, receivablesOriginLinkTextEnum);

        FieldAttrEntity newFieldAttrEntitySourceLinkText = InitExplainsHelper.setField(paymentEnumClass, paymentEnumAttrs, sourceLinkTextEnum);

        HashMap<String, FieldAttrEntity> shouldAddAttrMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        shouldAddAttrMap.put(redAmountEnum.getAttr(), newFieldAttrEntityRedAmount);
        shouldAddAttrMap.put(sourceEnum.getAttr(), newFieldAttrEntitySource);
        shouldAddAttrMap.put(generationDateEnum.getAttr(), newFieldAttrEntityGeneration);
        shouldAddAttrMap.put(receivablePeriodEnum.getAttr(), newFieldAttrEntityReceivable);
        shouldAddAttrMap.put(coUserIdEnum.getAttr(), newFieldAttrEntityCoUserId);
        shouldAddAttrMap.put(sourceEnum.getAttr(), newFieldAttrEntitySource);
        shouldAddAttrMap.put(receivableIsRedEnum.getAttr(), newFieldAttrEntityReceivableIsRed);
        shouldAddAttrMap.put(receivableHasRedEnum.getAttr(), newFieldAttrEntityReceivableHasRed);
        shouldAddAttrMap.put(sourceRefTypeEnum.getAttr(), newFieldAttrEntitySourceRefType);
        shouldAddAttrMap.put(receivablesOriginIdEnum.getAttr(), newFieldAttrEntityreceivablesOriginId);
        shouldAddAttrMap.put(receivablesOriginLinkTextEnum.getAttr(), newFieldAttrEntityReceivablesOriginLinkText);
        shouldAddAttrMap.put(sourceLinkTextEnum.getAttr(), newFieldAttrEntitySourceLinkText);
        return shouldAddAttrMap;
     }


    /**
     * 付款单模板解释
     * @param paymentSheetExplainsSingleDTO
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/paySheet/explains", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String paySheetExplains(@RequestBody @Valid PaymentSheetExplainsSingleDTO paymentSheetExplainsSingleDTO) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(paymentSheetExplainsSingleDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            LOG.warn("-----------paySheetExplains start 开始执行付款单模板解释处理脚本-----------");
            long timeStart = DateTimeUtil.getInt();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Integer start = 0;
            //每次查询100条
            Integer pageNum = 300;
            Long idGte = 0L;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            param.put("del", 0);
            //获取解释需要更新的值
            XbbRefTypeEnum paySheetEnum = XbbRefTypeEnum.PAY_SHEET;
            List<FieldAttrEntity> fieldAttrEntities = InitExplainsHelper.initFieldAttrEntity(paySheetEnum.getAlias(),null, null, 0);
            String fieldAttrStr = JSON.toJSONString(fieldAttrEntities);
            param.put("saasMark", SaasMarkEnum.SAAS.getCode());
            param.put("businessType", paySheetEnum.getCode());
            Integer planBusinessType = XbbRefTypeEnum.PAY_PLAN.getCode();
            String attrMethodPlan = PayPlanEnum.PAYMENT_METHOD.getAttr();
            String attrMethodSheet = PaySheetEnum.PAY_TYPE.getAttr();
            while (true) {
                param.put("idGte", idGte);
                LOG.warn("处理进度：已处理到 tb_paas_form_explain.id = " + idGte);
                //付款单的表单解释列表
                List<PaasFormExplainEntity> sheetExplainList = paasFormExplainModel.list(param);
                if (sheetExplainList == null || sheetExplainList.size() == 0) {
                    break;
                }
                idGte = sheetExplainList.get(sheetExplainList.size() - 1).getId();
                Set<String> corpidSet = new HashSet<>();
                for (PaasFormExplainEntity explainEntity : sheetExplainList) {
                    corpidSet.add(explainEntity.getCorpid());
                }
                //付款计划表单解释列表
                List<PaasFormExplainEntity> planExplainList = scriptHelper.findExplainByCorpidSet(planBusinessType, corpidSet);
                //公司id-支付方式map
                Map<String, List<ItemPoJo>> corpidMethodMap = new HashMap<>(planExplainList.size());
                for (PaasFormExplainEntity paymentExplainEntity : planExplainList) {
                    String corpid = paymentExplainEntity.getCorpid();
                    //付款计划字段列表
                    List<FieldAttrEntity> explains = JSONArray.parseArray(paymentExplainEntity.getExplains(), FieldAttrEntity.class);
                    for (FieldAttrEntity fieldAttrEntity : explains) {
                        String attr = fieldAttrEntity.getAttr();
                        //付款计划支付方式字段
                        if (Objects.equals(attrMethodPlan, attr)) {
                            List<ItemPoJo> items = fieldAttrEntity.getItems();
                            corpidMethodMap.put(corpid, items);
                        }
                    }
                }
                for (PaasFormExplainEntity explainEntity : sheetExplainList) {
                    //处理付款单的支付方式字段-置为付款计划的支付方式
                    String corpid = explainEntity.getCorpid();
                    List<ItemPoJo> itemPoJos = corpidMethodMap.get(corpid);
                    if (CollectionsUtil.isNotEmpty(itemPoJos)) {
                        List<FieldAttrEntity> paymentSheetExplainList = JSONArray.parseArray(fieldAttrStr, FieldAttrEntity.class);
                        for (FieldAttrEntity fieldAttrEntity : paymentSheetExplainList) {
                            String attr = fieldAttrEntity.getAttr();
                            if (Objects.equals(attrMethodSheet, attr)) {
                                fieldAttrEntity.setItems(itemPoJos);
                            }
                        }
                        String paymentSheetExplains = JSON.toJSONString(paymentSheetExplainList);
                        explainEntity.setExplains(paymentSheetExplains);
                    } else {
                        //更新成默认的解释
                        explainEntity.setExplains(fieldAttrStr);
                    }
                }
                //更新解释
                paasFormExplainModel.updateBatchExplains(sheetExplainList);
            }
            LOG.warn("-----------time consume paySheetExplains:" + (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/paySheet/explains over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/paySheet/explains error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    @RequestMapping(value = "/customer/statement/init", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String customerStatementInit(@RequestBody @Valid InitCustomerStatementDTO initCustomerStatementDTO) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(initCustomerStatementDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        Integer initFlag = initCustomerStatementDTO.getInitFlag();
        Long timestamp = initCustomerStatementDTO.getTimestamp();
        List<String> corpidList = initCustomerStatementDTO.getCorpidList();
        //参数校验
        if (Objects.equals(initFlag, BasicConstant.ONE) || Objects.equals(initFlag, BasicConstant.TWO)) {
            //1 代表前置执行脚本时的初始化，直接插入数据，【必须传timestamp】限制查询的结束时间
            //2 代表增量处理，只处理增量部分的数据。【必须传timestamp】，且与1对应（与1传的timestamp值必须一致）。限制查询的开始时间，且不能限制del条件（把del的状态变更也要处理到statement表），需与statement表数据匹配是插入还是更新（只更改关联名称、del状态）
            if (Objects.isNull(timestamp)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), "增量时间标识timestamp不能为空");
            }
            if (Objects.equals(initFlag, BasicConstant.ONE)) {
                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                int count = customerStatementModel.getEntitysCount(param);
                if (count > 0) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), "对账单初始化中，或前置执行已触发，无需再次初始化");
                }
            }
            if (CollectionUtils.isEmpty(corpidList)) {
                corpidList = companyModel.getCorpidList();
                initCustomerStatementDTO.setCorpidList(corpidList);
            }
        } else {
            //非1、2则代表修复对账记录，【必须传corpidList】按公司处理，不限制时间，且要排重（对已存在的记录更新）
            if (CollectionUtils.isEmpty(corpidList)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), "要处理的公司id集合corpidList不能为空");
            } else if (corpidList.size() > BasicConstant.ONE) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), "处理客户对账记录时，corpidList只能传1个公司的id");
            }
        }
        try {
            /**
             （1）使用线程方式执行脚本：对账记录只依赖回款单、应收款业务数据的一些核心字段，且上线前后逻辑的变动不会影响这些字段的存值格式，因此使用线程
             （2）做成增量方式：要把回款单、应收款全部打入对账记录，数据量较大，因此需要前置执行。前置执行则势必需要有对应的增量处理（上线后执行），处理前置到停服期间的增量数据
             */
            //线程方式-处理回款单对账记录
            scriptHelper.handleCustomerStatementAsync4Sheet(initCustomerStatementDTO);
            //线程方式-处理应收款对账记录
            scriptHelper.handleCustomerStatementAsync4Payment(initCustomerStatementDTO);
        } catch (Exception e) {
            LOG.error("initCustomerStatement", e);
        }
        LOG.warn("-----------线程触发完毕：该脚本是线程处理（不影响主流程），结束标记意义不大-----------");
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    @RequestMapping(value = "/customer/statement/init/batch", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String customerStatementInitBatch(@RequestBody @Valid InitCustomerStatementDTO initCustomerStatementDTO) throws Exception {
        if (!proBaseConfig.getXbbCode().equals(initCustomerStatementDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
        }
        Integer initFlag = initCustomerStatementDTO.getInitFlag();
        Long timestamp = initCustomerStatementDTO.getTimestamp();
        //参数校验
        if (Objects.equals(initFlag, BasicConstant.ONE) || Objects.equals(initFlag, BasicConstant.TWO)) {
            //1 代表前置执行脚本时的初始化，直接插入数据，【必须传timestamp】限制查询的结束时间
            //2 代表增量处理，只处理增量部分的数据。【必须传timestamp】，且与1对应（与1传的timestamp值必须一致）。限制查询的开始时间，且不能限制del条件（把del的状态变更也要处理到statement表），需与statement表数据匹配是插入还是更新（只更改关联名称、del状态）
            if (Objects.isNull(timestamp)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), "增量时间标识timestamp不能为空");
            }
            if (Objects.equals(initFlag, BasicConstant.ONE)) {
                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                int count = customerStatementModel.getEntitysCount(param);
                if (count > 0) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), "对账单初始化中，或前置执行已触发，无需再次初始化");
                }
            }
        } else {
            //非1、2则代表修复对账记录，【必须传corpidList】按公司处理，不限制时间，且要排重（对已存在的记录更新）
            String paramCorpid = initCustomerStatementDTO.getParamCorpid();
            if (StringUtil.isEmpty(paramCorpid)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001.getCode(), "要处理公司id（paramCorpid的值）不能为空");
            }
        }
        try {
            /**
             （1）使用线程方式执行脚本：对账记录只依赖回款单、应收款业务数据的一些核心字段，且上线前后逻辑的变动不会影响这些字段的存值格式，因此使用线程
             （2）做成增量方式：要把回款单、应收款全部打入对账记录，数据量较大，因此需要前置执行。前置执行则势必需要有对应的增量处理（上线后执行），处理前置到停服期间的增量数据
             */
            //线程方式-处理回款单对账记录，只批量入库不处理es，入库处理后再处理es
            scriptHelper.handleCustomerStatementAsyncBatch4Sheet(initCustomerStatementDTO);
            //线程方式-处理应收款对账记录
            scriptHelper.handleCustomerStatementAsyncBatch4Payment(initCustomerStatementDTO);
        } catch (Exception e) {
            LOG.error("initCustomerStatement", e);
        }
        LOG.warn("-----------线程触发完毕：该脚本是线程处理（不影响主流程），结束标记意义不大-----------");
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 付款单数据处理
     * @param paymentSheetDataDTO
     * @param br
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/paySheet/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String paySheetData(@RequestBody @Valid PaymentSheetDataDTO paymentSheetDataDTO, BindingResult br) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(paymentSheetDataDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            LOG.warn("-----------paySheetData start 开始执行付款单数据处理脚本-----------");
            long timeStart = DateTimeUtil.getInt();
            List<String> corpidList = paymentSheetDataDTO.getCorpidList();
            if (CollectionsUtil.isEmpty(corpidList)) {
                corpidList = payPlanSheetModel.getCorpidList();
            }
            int corpSize = corpidList.size();
            LOG.warn("数据库中共有" + corpSize + "家公司的付款单数据");
            int corpidCount = 0;
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Integer start = 0;
            //每次查询100条
            Integer pageNum = 500;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            param.put("del", 0);
            String attrPlan = PaySheetEnum.PAY_PLAN.getAttr();
            String attrPlanLink = PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr();
            //更新付款单数据的formId和menuId
            int businessType = XbbRefTypeEnum.PAY_SHEET.getCode();
            String columns = "id, menu_id";
            for (String corpid : corpidList) {
                PaasFormEntityExt formEntity = paasFormModel.getByBusinessType(businessType, corpid, columns);
                if (formEntity == null) {
                    continue;
                }
                Long menuId = formEntity.getMenuId();
                Long formId = formEntity.getId();
                FundAccountEntity fundAccountEntity = fundAccountModel.getDefaultFundAccount(corpid);
                Long accountId = fundAccountEntity == null ? null : fundAccountEntity.getId();
                Long idGte = 0L;
                param.put(ParameterConstant.CORPID, corpid);
                while (true) {
                    param.put("idGte", idGte);
                    LOG.warn("处理进度：已处理到 tb_saas_pay_sheet.id = " + idGte);
                    List<PayPlanSheetEntityExt> sheetList = payPlanSheetModel.findEntitys(param);
                    if (sheetList == null || sheetList.size() == 0) {
                        break;
                    }
                    idGte = sheetList.get(sheetList.size() - 1).getId();

                    Set<Long> planIdSet = new HashSet<>(sheetList.size());
                    for (PaasFormDataEntity dataEntity : sheetList) {
                        JSONObject data = dataEntity.getData();
                        if (data == null) {
                            continue;
                        }
                        //付款计划id
                        scriptHelper.getAssociatedConditions4Data(planIdSet, data, attrPlan, attrPlanLink);
                    }
                    //包装付款计划关联关系-采购合同、供应商等信息可以通过付款计划的data得到
                    Map<Long, String> planIdNoMap = new HashMap<>(planIdSet.size());
                    Map<Long, JSONObject> planIdDataMap = new HashMap<>(planIdSet.size());
                    getPlanInfo(corpid, planIdSet, planIdNoMap, planIdDataMap);
                    //处理字段
                    List<PayPlanSheetEntityExt> packageSheetList = disposeField(sheetList, planIdNoMap, planIdDataMap, menuId, formId, accountId);
                    // payPlanSheetModel.updateBatchForAll(packageSheetList, corpid, false);
                }
                ++corpidCount;
                LOG.warn("公司" + corpidCount + "/" + corpSize);
            }
            LOG.warn("-----------time consume paySheetData:" + (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/paySheet/data over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/paySheet/data error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 审批中付款单数据处理
     * @param paymentSheetDataDTO
     * @param br
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/paySheet/process/data", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String paySheetProcessData(@RequestBody @Valid PaymentSheetDataDTO paymentSheetDataDTO, BindingResult br) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(paymentSheetDataDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            LOG.warn("-----------paySheetProcessData start 开始执行审批中付款单数据处理脚本-----------");
            long timeStart = DateTimeUtil.getInt();
            List<String> corpidList = paymentSheetDataDTO.getCorpidList();
            if (CollectionsUtil.isEmpty(corpidList)) {
                corpidList = paasProcessDataModel.getCorpidList(XbbRefTypeEnum.PAY_SHEET.getCode());
            }
            if (CollectionsUtil.isEmpty(corpidList)) {
                return "无审批中付款单数据要处理";
            }
            int corpSize = corpidList.size();
            LOG.warn("数据库中共有" + corpSize + "家公司的审批中付款单数据");
            int corpidCount = 0;
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Integer start = 0;
            //每次查询100条
            Integer pageNum = 500;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            param.put("del", 0);
            String attrPlan = PaySheetEnum.PAY_PLAN.getAttr();
            String attrPlanLink = PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr();
            //更新审批中付款单数据
            int businessType = XbbRefTypeEnum.PAY_SHEET.getCode();
            param.put("businessType", businessType);
            param.put("saasMark", SaasMarkEnum.SAAS.getCode());
            String columns = "id, menu_id";
            for (String corpid : corpidList) {
                Long idGte = 0L;
                param.put(ParameterConstant.CORPID, corpid);
                while (true) {
                    param.put("idGte", idGte);
                    LOG.warn("处理进度：已处理到 tb_paas_process_data.id = " + idGte);
                    List<PaasProcessDataEntity> processDataList = paasProcessDataModel.list(param);
                    if (processDataList == null || processDataList.size() == 0) {
                        break;
                    }
                    idGte = processDataList.get(processDataList.size() - 1).getId();

                    FundAccountEntity fundAccountEntity = fundAccountModel.getDefaultFundAccount(corpid);
                    Long accountId = fundAccountEntity == null ? null : fundAccountEntity.getId();
                    String accountAttr = PaySheetEnum.FUND_ACCOUNT.getAttr();
                    Set<Long> planIdSet = new HashSet<>(processDataList.size());
                    for (PaasProcessDataEntity entity : processDataList) {
                        JSONObject data = JSON.parseObject(entity.getData());
                        if (data == null) {
                            continue;
                        }
                        //付款计划id
                        scriptHelper.getAssociatedConditions4Data(planIdSet, data, attrPlan, attrPlanLink);
                    }
                    //包装付款计划关联关系-采购合同、供应商等信息可以通过付款计划的data得到
                    Map<Long, String> planIdNoMap = new HashMap<>(planIdSet.size());
                    Map<Long, JSONObject> planIdDataMap = new HashMap<>(planIdSet.size());
                    getPlanInfo(corpid, planIdSet, planIdNoMap, planIdDataMap);
                    //处理data内字段
                    List<PaasProcessDataEntity> packageProcessSheetList = new ArrayList<>();
                    for (PaasProcessDataEntity entity : processDataList) {
                        JSONObject data = JSON.parseObject(entity.getData());
                        if (data == null) {
                            continue;
                        }
                        //处理data内字段
                        disposeFieldByData(entity.getFormDataId(), planIdNoMap, planIdDataMap, data);
                        //资金账户id
                        String account = data.getString(accountAttr);
                        if (accountId != null && StringUtil.isEmpty(account)) {
                            data.put(accountAttr, accountId.toString());
                        }
                        entity.setData(JSONObject.toJSONString(data));
                        packageProcessSheetList.add(entity);
                    }
                    paasProcessDataModel.updateBatchForAll(packageProcessSheetList, corpid);
                }
                ++corpidCount;
                LOG.warn("公司" + corpidCount + "/" + corpSize);
            }
            LOG.warn("-----------time consume paySheetProcessData:" + (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/paySheet/process/data over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/paySheet/process/data error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 通过回款单更新客户的预收款余额
     * @param fixSheetDTO
     * @param br
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/fix/paymentBalance", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixPaymentBalance(@RequestBody @Valid FixSheetDTO fixSheetDTO, BindingResult br) throws Exception {
        XbbResponse<ScriptVO> response;
        String msg = "success";
        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
            return JSON.toJSONString(response);
        } else {
            FundFixDataDTO fundFixDataDTO = new FundFixDataDTO();
            BeanUtil.copyProperties(fixSheetDTO, fundFixDataDTO);
            fundFixDataDTO.setDataIdList(fixSheetDTO.getLinkIdList());
            msg = fundFixDataService.repairPrepaymentBalance(fundFixDataDTO);
        }
        return JSON.toJSONString("{\"msg\":" + msg + "}");
    }

    /**
     * 通过付款单更新供应商的预付款余额
     * @param fixSheetDTO
     * @param br
     * @return java.lang.String
     * @throws Exception
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    @RequestMapping(value = "/fix/payBalance", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixPayBalance(@RequestBody @Valid FixSheetDTO fixSheetDTO, BindingResult br) throws Exception {
        XbbResponse<ScriptVO> response;
        String msg = "success";
        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
            return JSON.toJSONString(response);
        } else {
            FundFixDataDTO fundFixDataDTO = new FundFixDataDTO();
            BeanUtil.copyProperties(fixSheetDTO, fundFixDataDTO);
            fundFixDataDTO.setDataIdList(fixSheetDTO.getLinkIdList());
            msg = fundFixDataService.repairPrepayBalance(fundFixDataDTO);
        }
        return JSON.toJSONString("{\"msg\":" + msg + "}");
    }


    /**
     * 付款计划id-付款计划编号-map
     *
     * @param corpid 公司id
     * @param planIdSet 付款计划id集合
     * @param planIdNoMap 付款计划id-付款计划编号-map
     * @param planIdDataMap 付款计划id-付款data
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void getPlanInfo(String corpid, Set<Long> planIdSet, Map<Long, String> planIdNoMap, Map<Long, JSONObject> planIdDataMap) {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("idIn", planIdSet);
        List<PayPlanEntityExt> list = payPlanModel.findEntitys(param);
        for (PaasFormDataEntity entity : list) {
            Long id = entity.getId();
            JSONObject data = JsonHelperUtil.getJSONObject(entity.getData());
            data.put(BasicConstant.DATAID, entity.getId());
            planIdNoMap.put(id, entity.getSerialNo());
            planIdDataMap.put(id, data);
        }
    }

    /**
     * 处理字段
     * @param sheetList 付款单列表
     * @param planIdNoMap 付款计划id-付款计划编号
     * @param planIdDataMap 付款计划id-付款计划data
     * @param menuId 菜单id
     * @param formId 表单id
     * @param accountId 资金账户id
     * @return java.util.List<com.xbongbong.saas.domain.entity.ext.PayPlanSheetEntityExt>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<PayPlanSheetEntityExt> disposeField(List<PayPlanSheetEntityExt> sheetList, Map<Long, String> planIdNoMap, Map<Long, JSONObject> planIdDataMap, Long menuId, Long formId, Long accountId) {
        List<PayPlanSheetEntityExt> packageSheetList = new ArrayList<>();
        String accountAttr = PaySheetEnum.FUND_ACCOUNT.getAttr();
        for (PayPlanSheetEntityExt entityExt : sheetList) {
            String uuid = entityExt.getUuid();
            if (!StringUtil.isEmpty(uuid)) {
                //已经处理过
                continue;
            }
            entityExt.setMenuId(menuId);
            entityExt.setFormId(formId);
            JSONObject data = entityExt.getData();
            //处理data内字段
            disposeFieldByData(entityExt.getId(), planIdNoMap, planIdDataMap, data);
            //资金账户id
            String account = data.getString(accountAttr);
            if (accountId != null && StringUtil.isEmpty(account)) {
                data.put(accountAttr, accountId.toString());
            }
            //设置uuid
            entityExt.setUuid(UUID.randomUUID().toString());
            packageSheetList.add(entityExt);
        }
        return packageSheetList;
    }

    /**
     * 处理付款单的data内字段
     * @param dataId 数据id
     * @param planIdNoMap 付款计划id-付款计划编号
     * @param planIdDataMap 付款计划id-付款计划data
     * @param data 付款单data
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void disposeFieldByData(Long dataId, Map<Long, String> planIdNoMap, Map<Long, JSONObject> planIdDataMap, JSONObject data) {
        //处理类型字段（text_10）
        String sheetType = data.getString(PaySheetEnum.PAY_SHEET_TYPE.getAttr());
        if (StringUtil.isNotEmpty(sheetType)) {
            //付款单类型不为空，则说明已经刷过，不用再处理
            return;
        } else {
            data.put(PaySheetEnum.PAY_SHEET_TYPE.getAttr(), PaySheetTypeEnum.WRITE_OFF_PAY.getCode());
        }
        String planIdStr = data.getString(PaySheetEnum.PAY_PLAN.getAttr());
        JSONObject planData = null;
        if (StringUtil.isNotEmpty(planIdStr) && !planIdStr.contains(ChartConstant.LEFT_PARENTHESES)) {
            //处理关联付款计划的编号（是数组）
            Long planId = StringUtil.StringToLong(planIdStr, -1L);
            String planSerialNo = planIdNoMap.get(planId);
            planData = planIdDataMap.get(planId);
            //付款单、付款计划都为红冲
            JSONArray planIdArray = new JSONArray();
            planIdArray.add(planId);
            data.put(PaySheetEnum.PAY_PLAN.getAttr(), planIdArray);
            if (planSerialNo != null) {
                JSONArray jsonArray = new JSONArray();
                jsonArray.add(planSerialNo);
                data.put(PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr(), jsonArray);
            }
        }else if (StringUtil.isNotEmpty(planIdStr) && planIdStr.contains(ChartConstant.LEFT_PARENTHESES)) {
            JSONArray jsonArray = JSONArray.parseArray(planIdStr);
            Set<Long> idSet = new HashSet();
            if (Objects.nonNull(jsonArray)) {
                for (Object object : jsonArray) {
                    if (object != null) {
                        Long id = StringUtil.StringToLong(object.toString());
                        idSet.add(id);
                    }
                }
                data.put(PaySheetEnum.PAY_PLAN.getAttr(), idSet);
                JSONArray linkTextArray = JSONArray.parseArray(data.getString(PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr()));
                if (Objects.nonNull(linkTextArray)) {
                    JSONArray newTextArray = new JSONArray();
                    for (Object object : linkTextArray) {
                        if (object != null) {
                            newTextArray.add(object);
                        }
                    }
                    data.put(PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr(), newTextArray);
                }
            }
        }
        //根据付款计划，处理关联供应商、关联采购合同
        String contractIdStr = data.getString(PaySheetEnum.LINK_PURCHASE.getAttr());
        if (planData != null) {
            String supplierIdStr = planData.getString(PayPlanEnum.LINK_SUPPLIER.getAttr());
            Long supplierId = null;
            if (StringUtil.isNotEmpty(supplierIdStr)) {
                supplierId = StringUtil.StringToLong(supplierIdStr);
            }
            data.put(PaySheetEnum.LINK_SUPPLIER.getAttr(), supplierId);
            String supplierName = planData.getString(PayPlanEnum.LINK_SUPPLIER_LINK_TEXT.getAttr());
            data.put(PaySheetEnum.SUPPLIER_LINK_TEXT.getAttr(), supplierName);
            //处理关联采购合同及合同编号（是数组）
            if (StringUtil.isNotEmpty(contractIdStr)) {
                Long contractId = StringUtil.StringToLong(contractIdStr);
                String contractNo = planData.getString(PayPlanEnum.LINK_PURCHASE_LINK_TEXT.getAttr());
                JSONArray contractIdArray = new JSONArray();
                JSONArray contractSerialNoArray = new JSONArray();
                //合同id
                contractIdArray.add(contractId);
                //合同编号
                contractSerialNoArray.add(contractNo);
                data.put(PaySheetEnum.LINK_PURCHASE.getAttr(), contractIdArray);
                data.put(PaySheetEnum.PURCHASE_LINK_TEXT.getAttr(), contractSerialNoArray);
            }
        } else if (StringUtil.isNotEmpty(contractIdStr) && !contractIdStr.contains(ChartConstant.LEFT_PARENTHESES)) {
            Long contractId = StringUtil.StringToLong(contractIdStr);
            JSONArray contractIdArray = new JSONArray();
            //合同id
            contractIdArray.add(contractId);
            data.put(PaySheetEnum.LINK_PURCHASE.getAttr(), contractIdArray);
            LOG.warn("------当前付款单" + dataId + "关联的付款计划" + planIdStr + "不存在，只把其关联采购合同格式改为数组形式");
        }
        //处理核销金额
        double paymentAmount = data.getDoubleValue(PaySheetEnum.AMOUNT.getAttr());
        data.put(PaySheetEnum.WRITE_OFF_AMOUNT.getAttr(), paymentAmount);
        //处理归属人（array_1）
        Object belongIdObject = data.get("text_1");
        String belongId;
        if (belongIdObject instanceof JSONObject) {
            JSONObject belongIdJSONObject = (JSONObject) belongIdObject;
            belongId = belongIdJSONObject.getString("id");
            data.put("text_1", belongId);
        } else {
            belongId = data.getString("text_1");
        }
        if (belongId != null) {
            JSONArray belongIdArray = new JSONArray();
            belongIdArray.add(belongId);
            data.put(PaySheetEnum.BELONG_ID.getAttr(), belongIdArray);
        }
    }


    /**
     * 清除经销商应收款关于data_1字段的属性，“系统自动生成”这个备注以及editable这个字段需要默认为1，默认为可编辑
     *
     * @param paymentSheetExplainsSingleDTO
     * @param br
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/fix/distributorPaymentExplain", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixDistributorPaymentExplain(@RequestBody @Valid PaymentSheetExplainsSingleDTO paymentSheetExplainsSingleDTO, BindingResult br) throws Exception {
        try {
            if (!proBaseConfig.getXbbCode().equals(paymentSheetExplainsSingleDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            LOG.warn("-----------fixDistributorPaymentExplain start 开始执行修改经销商应收款中关于对data_1字段的处理-----------");
            long timeStart = DateTimeUtil.getInt();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Integer start = 0;
            //每次查询200条
            Integer pageNum = 200;
            param.put("start", start);
            param.put("pageNum", pageNum);
            param.put("orderByStr", "id asc");
            param.put("del", 0);
            param.put("distributorMark", DistributorMarkEnum.DISTRIBUTOR.getCode());
            param.put("businessType", XbbRefTypeEnum.PAYMENT.getCode());
            Long idGte = 0L;
            //移除默认的corpid为0存在redis中的解释
            removeDistributorDefaultExplainRedis(Arrays.asList(XbbRefTypeEnum.PAYMENT.getCode()));
            while (true) {
                param.put("idGte", idGte);
                LOG.warn("处理进度：已处理到 tb_paas_form_explain.id = " + idGte);
                List<PaasFormExplainEntity> paasFormExplainEntities = paasFormExplainModel.getNoHandleExplainsByFindEntitys(param);
                if (CollectionsUtil.isEmpty(paasFormExplainEntities)) {
                    break;
                }
                idGte = paasFormExplainEntities.get(paasFormExplainEntities.size() - 1).getId();
                List<PaasFormExplainEntity> updateList = new ArrayList<>();
                for (PaasFormExplainEntity paasFormExplainEntity : paasFormExplainEntities) {
                    String explains = paasFormExplainEntity.getExplains();
                    if (Objects.nonNull(paasFormExplainEntity) && Objects.equals("[]", paasFormExplainEntity.getExplains())) {
                        continue;
                    }
                    List<FieldAttrEntity> fieldList = JSON.parseArray(explains, FieldAttrEntity.class);
                    for (FieldAttrEntity fieldAttrEntity : fieldList) {
                        if (Objects.equals(PaymentDistributorEnum.ESTIMATE_TIME.getAttr(), fieldAttrEntity.getAttr())) {
                            fieldAttrEntity.setUnableEditMemo(null);
                            fieldAttrEntity.setEditable(BasicConstant.ONE);
                        }else if (Objects.equals(PaymentDistributorEnum.SOURCE.getAttr(), fieldAttrEntity.getAttr())) {
                            fieldAttrEntity.setRequired(BasicConstant.ZERO);
                        }
                    }
                    String newExplainsStr = JSON.toJSONString(fieldList);
                    paasFormExplainEntity.setExplains(newExplainsStr);
                    updateList.add(paasFormExplainEntity);
                }
                if (CollectionsUtil.isNotEmpty(updateList)) {
                    paasFormExplainModel.updateBatchExplains(updateList);
                }
            }
            LOG.warn("-----------time consume distributorPaymentExplain:" + (DateTimeUtil.getInt() - timeStart));
            LOG.warn("-----------/script/fund/sheet/distributorPaymentExplain/process/data over");
        } catch (Exception e) {
            LOG.error("/script/fund/sheet/paySheet/distributorPaymentExplain/data error", e);
        }
        return JSON.toJSONString("{\"msg\":\"success\"}");
    }

    /**
     * 移除经销商默认存在redis中的解释
     *
     * @param businessTypes
     */
    private void removeDistributorDefaultExplainRedis(List<Integer> businessTypes) {
        String corpid = "0";
        for (Integer businessType : businessTypes) {
            String redisKey = corpid + "_" + businessType + "_" + DistributorMarkEnum.DISTRIBUTOR.getCode();
            paasRedisHelper.removeValue(RedisPrefixConstant.FIELD_EXPLANATION, redisKey);
        }
    }


    /**
     * 修复回款单data中关联合同隐藏字段“text_12” 为null 的数据
     * @param PaymentSheetDataDTO
     * @return
     * @throws XbbException
     */
    @RequestMapping(value = "/fixPaymentSheetData", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String fixPaymentSheetByData(@RequestBody @Valid PaymentSheetDataDTO paymentSheetDataDTO) throws XbbException {
        if (!proBaseConfig.getXbbCode().equals(paymentSheetDataDTO.getXbbCode())) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006, SystemErrorCodeEnum.API_ERROR_100006.getMsg());
        }
        LOG.warn("-------------fixPaymentSheetData begin-----------");
        long start = DateTimeUtil.getInt();
        LocaleContextHolder.setLocale(Locale.CHINA);
        //不支持全公司刷
        List<String> corpIdList = paymentSheetDataDTO.getCorpidList();
        if (CollectionsUtil.isEmpty(corpIdList)) {
            corpIdList = companyModel.getCorpidList();
        }
        if (Objects.isNull(corpIdList)) {
            return "";
        }
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.DEL, 0);
        param.put("contractSerialNo","");
        LOG.info("共有" + corpIdList.size() + "家数据");
        int i = 0;
        for (String corpid : corpIdList) {
            i++;
            param.put("corpid",corpid);
            LOG.info("现在是第" + i + "家公司的数据");
            List<PaymentSheetEntityExt> paymentSheetEntityExts = paymentSheetModel.findEntitys(param);
            if (CollectionsUtil.isEmpty(paymentSheetEntityExts)){
                continue;
            }
            List<UpdateDataEntity> updateDataEntityList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, List<Long>> sheetIdAndContractIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Set<Long> contractIdSet = new HashSet<>();
            for (PaymentSheetEntityExt paymentSheetEntityExt : paymentSheetEntityExts) {
                List<Long> tempList = new ArrayList<>();
                JSONObject data = paymentSheetEntityExt.getData();
                JSONArray contractIdArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, PaymentSheetEnum.CONTRACT.getAttr(),new JSONArray());
                for (Object o :contractIdArr){
                    tempList.add(Long.parseLong(o.toString()));
                }
                contractIdSet.addAll(tempList);
                sheetIdAndContractIdMap.put(paymentSheetEntityExt.getId(),tempList);
            }
            List<ContractEntityExt> contractEntityList = contractModel.findEntitysByIdIn(corpid,BasicConstant.ZERO,contractIdSet.stream().collect(Collectors.toList()));
            //将对应回款单的合同编号筛进text_12中
            Map<Long, String> contractIdMap = contractEntityList.stream().collect(Collectors.toMap(ContractEntityExt::getId,ContractEntityExt::getSerialNo));
            sheetIdAndContractIdMap.forEach((sheetId,contractIdList) -> {
                JSONObject updateData = new JSONObject();
                List<String> contractSeriaNo = new ArrayList<>();
                //通过合同id匹配编号
                if(CollectionsUtil.isNotEmpty(contractIdList) && contractIdMap != null){
                    contractIdList.forEach(id -> {
                        contractSeriaNo.add(contractIdMap.get(id));
                    });
                }
                //将匹配到的合同编号数组筛到更新的data
                updateData.put(PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr(),contractSeriaNo);
                if (!updateData.isEmpty()) {
                    updateDataEntityList.add(ExplainUtil.getUpdateData(sheetId, updateData, corpid));
                }
            });
            //根据公司id进行批量更新
            if (CollectionsUtil.isNotEmpty(updateDataEntityList)){
                paymentSheetModel.updateBatch(updateDataEntityList,corpid);
            }
        }
        JSONObject object = new JSONObject();
        object.put("success", "成功");
        LOG.warn("consume:" + (DateTimeUtil.getInt() - start));
        LOG.warn("-------------fixPaymentSheetData over-----------");
        return object.toJSONString();
    }

}
